package com.oocl.euc.core.maintenance.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.oocl.euc.core.builder.RoleAuthBuilder;
import com.oocl.euc.core.builder.RoleBuilder;
import com.oocl.euc.core.builder.UsersBuilder;
import com.oocl.euc.core.common.ITTestBase;
import com.oocl.euc.core.maintenance.model.Role;
import com.oocl.euc.core.maintenance.model.RoleAuth;
import com.oocl.euc.core.maintenance.model.Users;
import com.oocl.euc.core.maintenance.repository.RoleAuthRepository;
import com.oocl.euc.core.maintenance.repository.RoleRepository;
import com.oocl.euc.core.maintenance.repository.UsersRepository;
import com.oocl.euc.core.maintenance.service.SupportGroupService;
import com.oocl.euc.framework.base.persistence.oracle.SequenceIDPersistable;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;

import java.util.*;

import static org.hamcrest.core.Is.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

public class RoleControllerIT extends ITTestBase {
    @Autowired
    RoleBuilder roleBuilder;
    @Autowired
    ObjectMapper objectMapper;
    @Autowired
    RoleRepository roleRepository;
    @Autowired
    UsersRepository usersRepository;
    @Autowired
    RoleAuthBuilder roleAuthBuilder;
    @Autowired
    UsersBuilder usersBuilder;
    @Autowired
    RoleAuthRepository roleAuthRepository;
    @Autowired SupportGroupService supportGroupService;

    private List<String> usersIdList = new ArrayList<>();

    @After
    public void reset() {
        roleRepository.deleteAll();
        for(String id : usersIdList) {
            usersRepository.deleteById(id);
        }
        usersIdList.clear();
        roleAuthRepository.deleteAll();
    }

    @Test
    public void should_return_one_role_given_access_url_role_all_when_only_exist_one_role() throws Exception {
        Role role = roleBuilder.defaultBuilder()
                .withRoleName("Test Role Name")
                .withRoleDesc("Test Role Desc")
                .saveToDB();

        mockMvc.perform(get("/rest/role/all").contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$").isNotEmpty())
                .andExpect(jsonPath("$.length()", is(1)))
                .andExpect(jsonPath("$[0].roleName", is(role.getRoleName())));
    }

    @Test
    public void should_return_all_roles_given_access_url_role_all_when_exist_multiple_roles() throws Exception {
        Role firstRole = roleBuilder.defaultBuilder()
                .withRoleName("Test Role Name 1")
                .withRoleDesc("Test Role Desc 1")
                .saveToDB();
        Role secondRole = roleBuilder.defaultBuilder()
                .withRoleName("Test Role Name 2")
                .withRoleDesc("Test Role Desc 2")
                .saveToDB();

        mockMvc.perform(get("/rest/role/all").contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$").isNotEmpty())
                .andExpect(jsonPath("$.length()", is(2)))
                .andExpect(jsonPath("$[0].roleName", is(firstRole.getRoleName())))
                .andExpect(jsonPath("$[0].roleDesc", is(firstRole.getRoleDesc())))
                .andExpect(jsonPath("$[1].roleName", is(secondRole.getRoleName())))
                .andExpect(jsonPath("$[1].roleDesc", is(secondRole.getRoleDesc())));
    }

    @Test
    public void should_add_one_role_in_DB_given_access_url_add() throws Exception {
        Role role = roleBuilder.defaultBuilder()
                .withRoleName("Test add Role Name")
                .withRoleDesc("Test add Role Desc")
                .create();
        String requestJson = objectMapper.writeValueAsString(role);

        mockMvc.perform(post("/rest/role/add").content(requestJson).contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$").isNotEmpty())
                .andExpect(jsonPath("$.roleName", is(role.getRoleName())))
                .andExpect(jsonPath("$.roleDesc", is(role.getRoleDesc())));
    }

    @Test
    public void should_delete_one_role_in_DB_given_access_url_del_roleId() throws Exception {
        Role firstRole = roleBuilder.defaultBuilder()
                .withRoleName("Test first Role Name")
                .withRoleDesc("Test first Role Desc")
                .saveToDB();
        roleBuilder.defaultBuilder()
                .withRoleName("Test second Role Name")
                .withRoleDesc("Test second Role Desc")
                .saveToDB();

        mockMvc.perform(delete("/rest/role/del/{roleId}", firstRole.getId()).contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$").value(firstRole.getId()));
    }

    @Test
    public void should_add_one_roleAuthorization_in_DB_given_access_url_add_authorization_with_domainId() throws Exception {
        String domainId = "XIEMI2";
        Users users = usersBuilder.defaultBuilder()
                .withDomainId(domainId)
                .saveToDB();
        usersIdList.add(users.getStaffId());
        Role role = roleBuilder.defaultBuilder()
                .saveToDB();
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("roleId", role.getId());
        requestBody.put("domainIdOrGroupName", domainId);

        String requestJson = objectMapper.writeValueAsString(requestBody);

        mockMvc.perform(post("/rest/role/add/authorization").content(requestJson).contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$").isNotEmpty())
                .andExpect(jsonPath("$.id", is(Integer.valueOf(role.getId().toString()))))
                .andExpect(jsonPath("$.roleName", is(role.getRoleName())))
                .andExpect(jsonPath("$.roleAuths.length()", is(1)))
                .andExpect(jsonPath("$.roleAuths[0].userName", is(users.getDomainId())));
    }

    @Test
    public void should_add_one_roleAuthorization_in_DB_given_access_url_add_authorization_with_group_name() throws Exception {
        String groupName = "all_users";
        Role role = roleBuilder.defaultBuilder()
            .saveToDB();
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("roleId", role.getId());
        requestBody.put("domainIdOrGroupName", groupName);

        String requestJson = objectMapper.writeValueAsString(requestBody);

        mockMvc.perform(post("/rest/role/add/authorization").content(requestJson).contentType(MediaType.APPLICATION_JSON_UTF8))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$").isNotEmpty())
            .andExpect(jsonPath("$.id", is(Integer.valueOf(role.getId().toString()))))
            .andExpect(jsonPath("$.roleName", is(role.getRoleName())))
            .andExpect(jsonPath("$.roleAuths.length()", is(1)))
            .andExpect(jsonPath("$.roleAuths[0].userName", is(groupName.toUpperCase())));
    }

    @Test
    public void should_delete_role_authorization_by_roleAuthorizationId_given_access_url_del_authorization() throws Exception {
        String domainId1 = "XIEMI2";
        String domainId2 = "CHENCO7";
        Users user1 = usersBuilder.defaultBuilder()
                .withStaffId("1")
                .withDomainId(domainId1)
                .saveToDB();
        usersIdList.add(user1.getStaffId());
        Users user2 = usersBuilder.defaultBuilder()
                .withStaffId("2")
                .withDomainId(domainId2)
                .saveToDB();
        usersIdList.add(user2.getStaffId());
        RoleAuth roleAuth1 = roleAuthBuilder.defaultBuilder()
                .withUsers(user1)
                .create();
        RoleAuth roleAuth2 = roleAuthBuilder.defaultBuilder()
                .withUsers(user2)
                .create();
        Set<RoleAuth> roleAuthSet = new HashSet<RoleAuth>() {{
            add(roleAuth1);
            add(roleAuth2);
        }};
        Role role = roleBuilder.defaultBuilder()
                .withRoleAuths(roleAuthSet)
                .withRoleName("Test Role Name")
                .saveToDB();
        Map<String, Object> requestBody = new HashMap<String, Object>() {{
            put("roleId", role.getId());
            put("domainIdOrGroupName", domainId1);
        }};
        long roleAuthorizationId1 = role.getRoleAuths().stream()
                .findFirst()
                .map(SequenceIDPersistable::getId)
                .orElse(1L);

        Assert.assertEquals(2, roleAuthRepository.findAll().size());

        String requestJson = objectMapper.writeValueAsString(requestBody);

        mockMvc.perform(delete("/rest/role/del/authorization/{roleAuthorizationId}", roleAuthorizationId1)
                .content(requestJson).contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", is(Integer.valueOf(roleAuthorizationId1 + ""))));
        Assert.assertEquals(1, roleAuthRepository.findAll().size());
    }

    @Test
    public void should_update_role_given_access_url_update_when_exist_one_role_in_DB() throws Exception {
        Role existRole = roleBuilder.defaultBuilder()
                .withRoleName("Test Role Name")
                .withRoleDesc("Test Role Desc")
                .saveToDB();
        existRole.setRoleDesc("Test Update Role Desc");
        String requestJson = objectMapper.writeValueAsString(existRole);

        mockMvc.perform(put("/rest/role/update").content(requestJson).contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id", is(Integer.valueOf(existRole.getId() + ""))))
                .andExpect(jsonPath("$.roleName", is(existRole.getRoleName())))
                .andExpect(jsonPath("$.roleDesc", is(existRole.getRoleDesc())));
    }
}
